home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
et
/
et3_0-a1.lha
/
et3
/
src
/
Port.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-26
|
19KB
|
929 lines
#ifdef __GNUG__
#pragma implementation
#endif
#include "Port.h"
#include "Class.h"
#include "String.h"
#include "Error.h"
#include "Picture.h"
#include "WindowSystem.h"
#include "DevBitmap.h"
#include "Math.h"
Port *Port::port, *Port::tbport;
Ink *Port::tbink= 0;
Point Port::tbpos, Port::tblastpos;
Rectangle Port::tbbbox;
static inline void ScaleRect(Port *p, Rectangle &r)
{
if (p->scale & 1) {
r.origin.x= (int) ((float)r.origin.x * p->scalex + 0.5);
r.origin.y= (int) ((float)r.origin.y * p->scaley + 0.5);
r.extent.x= (int) ((float)r.extent.x * p->scalex + 0.5);
r.extent.y= (int) ((float)r.extent.y * p->scaley + 0.5);
}
}
static inline void ScalePoint(Port *port, Point &p)
{
if (port->scale & 1) {
p.x= (int) ((float)p.x * port->scalex + 0.5);
p.y= (int) ((float)p.y * port->scaley + 0.5);
}
}
static inline void ScaleInt(Port *p, int &i)
{
if (p->scale & 1)
i= (int) ((float)i * p->scalex + 0.5);
}
GrState::GrState(Port *p)
{
if (p == 0)
port= Port::port;
else
port= p;
clip= port->cliprect;
origin= port->origin;
scalex= port->scalex;
scaley= port->scaley;
}
GrState::~GrState()
{
port->FlushMyText();
port->sfont= 0;
port->DevScale(scalex, scaley);
port->origin= origin;
port->Clip(clip);
}
//---- Port --------------------------------------------------------------------
NewMetaImpl(Port,SysEvtHandler, (T(depth), T(hascolor)));
Port::Port() : SysEvtHandler(-1)
{
PortInit();
hascolor= FALSE;
depth= gDepth;
}
Port::~Port()
{
if (port == this)
port= 0;
if (tbport == this)
tbport= 0;
}
void Port::PortInit()
{
scalex= scaley= 1.0;
scale= FALSE;
cliprect= Rectangle(0, 0, 30000, 30000);
origin= gPoint0;
SetNormal();
SetPenNormal();
SetTextNormal();
}
//---- abstract cursor methods -------------------------------------------------
GrCursor Port::SetCursor(GrCursor c)
{
return c;
}
GrCursor Port::SetWaitCursor(unsigned int, GrCursor c)
{
if (this)
return SetCursor(c);
return c;
}
GrCursor Port::GetCursor()
{
return eCrsNone;
}
//---- graphics context --------------------------------------------------------
void Port::SetPenNormal()
{
pensize= 1;
penink= gInkBlack;
pencap= eDefaultCap;
}
void Port::SetTextNormal()
{
textink= gInkBlack;
textfont= gSysFont;
}
//---- color halftoning (ordered dither) ---------------------------------------
extern Ink *Grey2Halftone(float f);
void Port::DevSetGrey(float f)
{
InstallInk(Grey2Halftone(f));
}
void Port::SetColor(RGBColor *cp)
{
if (HasColor()) {
DevSetColor(cp);
} else
DevSetGrey(cp->AsGreyLevel() / 255.0);
}
//---- clipping ----------------------------------------------------------------
void Port::InitClip()
{
FlushMyText();
sfont= 0;
DevScale(1.0, 1.0);
cliprect= Rectangle(0, 0, 30000, 30000);
DevClip(cliprect);
origin= gPoint0;
}
void Port::Clip(Rectangle r)
{
if (cliprect != r) {
FlushMyText();
cliprect= r;
DevClip(cliprect);
// scale|= 2;
}
}
void Port::ClipFurther(Rectangle r)
{
ScaleRect(this, r);
r.origin+= origin;
r.Clip(cliprect);
Clip(r);
}
void Port::Scale(float x, float y)
{
FlushMyText();
sfont= 0;
DevScale(x*scalex, y*scaley);
}
void Port::Translate(Point o)
{
ScalePoint(this, o);
origin+= o;
}
bool Port::Visible(Ink *ink, Rectangle &r)
{
if (ink != gInkNone && (WindowSystem::fullscreen || cliprect.Intersects(r))) {
if (scale & 2) {
DevClip(cliprect);
scale&= ~2;
}
FlushMyText();
InstallInk(ink);
return TRUE;
}
return FALSE;
}
//---- graphic primitives ------------------------------------------------------
void Port::StrokeLine(Ink *ink, int psz, GrLineCap cap, Point p1, Point p2)
{
if (ignore)
return;
if (scale & 1) {
ScalePoint(this, p1);
ScalePoint(this, p2);
ScaleInt(this, psz);
}
p1+= origin;
p2+= origin;
Rectangle rr= NormRect(p1,p2).Expand(Math::Max(2, psz)/2);
if (Visible(ink, rr))
DevStrokeLine(psz, &rr, cap, p1, p2);
}
void Port::StrokeRect(Ink *ink, int psz, Rectangle r)
{
if (ignore)
return;
if (scale & 1) {
ScaleRect(this, r);
ScaleInt(this, psz);
}
r.origin+= origin;
if (Visible(ink, r))
DevStrokeRect(psz, &r);
}
void Port::FillRect(Ink *ink, Rectangle r)
{
if (ignore)
return;
ScaleRect(this, r);
r.origin+= origin;
if (Visible(ink, r))
DevFillRect(&r);
}
void Port::StrokeOval(Ink *ink, int psz, Rectangle r)
{
if (ignore)
return;
if (scale & 1) {
ScaleRect(this, r);
ScaleInt(this, psz);
}
r.origin+= origin;
if (Visible(ink, r))
DevStrokeOval(psz, &r);
}
void Port::FillOval(Ink *ink, Rectangle r)
{
if (ignore)
return;
ScaleRect(this, r);
r.origin+= origin;
if (Visible(ink, r))
DevFillOval(&r);
}
void Port::StrokeRRect(Ink *ink, int psz, Rectangle r, Point dia)
{
if (ignore)
return;
if (scale & 1) {
ScaleRect(this, r);
ScalePoint(this, dia);
ScaleInt(this, psz);
}
r.origin+= origin;
if (Visible(ink, r))
DevStrokeRRect(psz, &r, dia);
}
void Port::FillRRect(Ink *ink, Rectangle r, Point dia)
{
if (ignore)
return;
if (scale & 1) {
ScaleRect(this, r);
ScalePoint(this, dia);
}
r.origin+= origin;
if (Visible(ink, r))
DevFillRRect(&r, dia);
}
void Port::StrokeWedge(Ink *ink, int psz, GrLineCap cap, Rectangle r, int s, int l)
{
if (ignore)
return;
if (l >= 360) {
StrokeOval(ink, psz, r);
return;
}
while (s < 0)
s+= 360;
while (s >= 360)
s-= 360;
if (scale & 1) {
ScaleRect(this, r);
ScaleInt(this, psz);
}
r.origin+= origin;
if (Visible(ink, r.WedgeBBox(s, l)))
DevStrokeWedge(psz, cap, &r, s, l);
}
void Port::FillWedge(Ink *ink, Rectangle r, int s, int l)
{
if (ignore)
return;
if (l >= 360) {
FillOval(ink, r);
return;
}
while (s < 0)
s+= 360;
while (s >= 360)
s-= 360;
ScaleRect(this, r);
r.origin+= origin;
if (Visible(ink, r.WedgeBBox(s, l)))
DevFillWedge(&r, s, l);
}
void Port::StrokePolygon(Point at, Ink *ink, Point *pts,
int npts, GrPolyType t, int psz, GrLineCap cap)
{
if (ignore)
return;
if (pts && npts > 0) {
Point *p= (Point*) Alloca(npts * sizeof(Point));
Rectangle r(BoundingBox(npts, pts, p));
r.origin+= at;
if (scale & 1) {
ScaleRect(this, r);
ScaleInt(this, psz);
for (int i= 0; i < npts; i++)
ScalePoint(this, p[i]);
}
r.origin+= origin;
if (Visible(ink, r))
DevStrokePolygon(&r, p, npts, t, psz, cap);
Freea(p);
}
}
void Port::FillPolygon(Point at, Ink *ink, Point *pts, int npts, GrPolyType t)
{
if (ignore)
return;
if (pts && npts > 0) {
Point *p= (Point*) Alloca(npts * sizeof(Point));
Rectangle r= BoundingBox(npts, pts, p);
r.origin+= at;
if (scale & 1) {
ScaleRect(this, r);
for (int i= 0; i < npts; i++)
ScalePoint(this, p[i]);
}
r.origin+= origin;
if (Visible(ink, r))
DevFillPolygon(&r, p, npts, t);
Freea(p);
}
}
void Port::ShowBitmap(Ink *ink, Rectangle r, Bitmap *bm)
{
if (ignore)
return;
ScaleRect(this, r);
r.origin+= origin;
if (Visible(ink, r))
DevShowBitmap(&r, bm);
}
void Port::ShowPicture(Rectangle r, Picture *pic)
{
if (ignore || pic == 0)
return;
ScaleRect(this, r);
r.origin+= origin;
if (Visible(gInkBlack, r))
DevShowPicture(&r, pic);
}
void Port::GiveHint(int code, int len, void *vp)
{
Rectangle r;
if (vp && len < 0)
len= strlen((const char*) vp) + 1;
switch (code) {
case eHintBatch:
r= *((Rectangle*) vp);
ScaleRect(this, r);
r.origin+= origin;
DevGiveHint(code, len, &r);
break;
default:
DevGiveHint(code, len, vp);
break;
}
}
//---- Text ---------------------------------------------------------------------
void Port::SetFamily(GrFont fid)
{
textfont= new_Font(fid, textfont->Size(), textfont->Face());
sfont= 0;
}
void Port::SetSize(int ps)
{
textfont= new_Font(textfont->Fid(), ps, textfont->Face());
sfont= 0;
}
void Port::SetFace(GrFace face)
{
textfont= new_Font(textfont->Fid(), textfont->Size(), face);
sfont= 0;
}
void Port::SetFont(Font *fd)
{
if (textfont != fd) {
textfont= fd;
sfont= 0;
}
}
//---- text batch --------------------------------------------------------------
void Port::flushtext()
{
if (scale & 2) {
DevClip(cliprect);
scale&= ~2;
}
InstallInk(tbink);
DevShowTextBatch(&tbbbox, tbpos+origin);
tbport= 0;
}
int Port::ShowChar(Font *fdp, Ink *ink, Point pos, byte c)
{
byte s[2];
s[0]= c;
s[1]= 0;
return ShowString(fdp, ink, pos, s, 1);
}
Ink *gTbInk;
int Port::ShowString(Font *fdp, Ink *ink, Point pos, byte *s, int l)
{
Rectangle rr;
register int w, sl= 0;
byte c, *ss= s;
int ll= l;
if (ignore || s == 0)
return 0;
if (fdp != lastfdp) {
lastfdp= fdp;
sfont= 0;
}
if (scale & 1) {
if (sfont == 0) {
int ss= fdp->Size();
ScaleInt(this, ss);
sfont= new_Font(fdp->Fid(), ss, fdp->Face());
}
fdp= sfont;
ScalePoint(this, pos);
}
rr.origin= pos;
rr.origin+= origin;
rr.origin.y-= fdp->Ascender();
rr.extent.y= fdp->Spacing();
while (l-- && (c= *s++)) {
rr.extent.x= w= fdp->Width(c);
if (WindowSystem::fullscreen || cliprect.Intersects(rr)) {
if (scale & 2) {
DevClip(cliprect);
scale&= ~2;
}
if (this != tbport || ink != tbink)
FlushAnyText();
gTbInk= ink;
DevShowChar(fdp, pos-tblastpos, c, &rr);
if (tbport == 0) {
tbport= this;
tbink= ink;
tbpos= pos;
tbbbox= rr;
} else
tbbbox.Merge(rr);
tblastpos.x= pos.x + w;
tblastpos.y= pos.y;
}
rr.origin.x+= w;
pos.x+= w;
sl+= w;
}
if (scale & 1)
return lastfdp->Width(ss, ll);
return sl;
}
//---- device dependent methods ------------------------------------------------
void Port::DevClip(Rectangle)
{
AbstractMethod("DevClip");
}
void Port::DevFillRect(Rectangle*)
{
AbstractMethod("DevFillRect");
}
void Port::DevShowBitmap(Rectangle*, struct Bitmap*)
{
AbstractMethod("DevShowBitmap");
}
void Port::DevShowChar(Font*, Point, byte, Rectangle*)
{
}
void Port::DevShowTextBatch(Rectangle*, Point)
{
AbstractMethod("DevShowTextBatch");
}
void Port::DevGiveHint(int, int, void*)
{
}
void Port::DevSetPattern(struct DevBitmap*)
{
}
void Port::DevSetColor(RGBColor*)
{
}
void Port::DevSetOther(int)
{
}
bool Port::DevImageCacheBegin(ImageCache*, Rectangle)
{
return TRUE; // need redraw
}
void Port::DevImageCacheEnd(ImageCache*)
{
}
bool Port::DevScale(float ssx, float ssy)
{
scalex= ssx;
scaley= ssy;
if (scalex != 1.0 || scaley != 1.0)
scale|= 1;
else
scale&= ~1;
return FALSE; // FALSE means: can't scale
}
void Port::DevShowPicture(Rectangle *r, Picture *pic)
{
pic->Show(r, this);
}
//---- window system independent graphic methods -------------------------------
Point Port::DevDrawArrow(int psz, Point s, Point e)
{
int arrowwidth= Math::Max(6, psz*4+2), arrowlen= Math::Max(9, psz*7);
Point c, pts[3], ee(arrowwidth/2, arrowlen);
if (Length(e-s) < arrowlen)
return e; // too short, give up
double ph= Phi(e-s), rot= Phi(ee), hyp= Length(ee);
pts[0]= e;
#ifdef PETERFIX
// special case: going straight down
pts[1]= e + Point(-(arrowwidth/2),-arrowlen);
pts[2]= e + Point(arrowwidth/2,-arrowlen);
#else
pts[1]= e + PolarToPoint(ph+rot, hyp, hyp);
pts[2]= e + PolarToPoint(ph-rot, hyp, hyp);
#endif
c= Half(pts[1]+pts[2]);
Rectangle rr= BoundingBox(3, pts);
DevFillPolygon2(&rr, pts, 3, ePolyDefault);
return c;
}
void Port::DevStrokeLine(int psz, Rectangle *r,
GrLineCap cap, Point start, Point end)
{
if (cap & eStartArrow)
start= DevDrawArrow(psz, end, start);
if (cap & eEndArrow)
end= DevDrawArrow(psz, start, end);
DevStrokeLine2(psz, r, cap, start, end);
}
void Port::DevStrokeRect(int psz, Rectangle *r)
{
int rpsz= (psz <= 0) ? 1 : psz;
rpsz*= 2;
if (rpsz >= r->extent.x || rpsz >= r->extent.y)
DevFillRect(r);
else
DevStrokeRect2(psz, r);
}
//---- splines -----------------------------------------------------------------
/*
* bez: Subdivide a Bezier spline, until it is thin enough to be
* considered a line. Store line point in static array points.
*/
#define FIX(a) (((int) (a)) << 16)
#define INT(a) (((a) + (1 << 15)) >> 16 )
#define DELTA 1
static void bez(Point **gPp, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
{
int maxx= Math::Max(x0, x3), minx= Math::Min(x0, x3), maxy= Math::Max(y0, y3), miny= Math::Min(y0, y3);
register int tx, ty;
if (x1 >= minx && x1 <= maxx && y1 >= miny && y1 <= maxy
&& x2 >= minx && x2 <= maxx && y2 >= miny && y2 <= maxy) {
register int ax, ay, dx= INT(x3-x0), dy= INT(y3-y0);
if (dx == 0 || dy == 0) {
(*gPp)->x= INT(x3);
(*gPp)++->y= INT(y3);
return;
}
ax= ((dy*INT(x1-x0))/dx)+INT(y0-y1);
ay= ((dx*INT(y1-y0))/dy)+INT(x0-x1);
if (Math::Abs(ax*ay) <= DELTA) {
(*gPp)->x= INT(x3);
(*gPp)++->y= INT(y3);
return;
}
ax= ((dy*INT(x2-x0))/dx)+INT(y0-y2);
ay= ((dx*INT(y2-y0))/dy)+INT(x0-x2);
if (Math::Abs(ax*ay) <= DELTA) {
(*gPp)->x= INT(x3);
(*gPp)++->y= INT(y3);
return;
}
}
tx= (x0 >> 3) + 3 * (x1 >> 3) + 3 * (x2 >> 3) + (x3 >> 3);
ty= (y0 >> 3) + 3 * (y1 >> 3) + 3 * (y2 >> 3) + (y3 >> 3);
bez(gPp, x0, y0, (x0 >> 1) + (x1 >> 1), (y0 >> 1) + (y1 >> 1),
(x0 >> 2) + (x1 >> 1) + (x2 >> 2),
(y0 >> 2) + (y1 >> 1) + (y2 >> 2),
tx, ty);
bez(gPp, tx, ty, (x3 >> 2) + (x2 >> 1) + (x1 >> 2),
(y3 >> 2) + (y2 >> 1) + (y1 >> 2),
(x3 >> 1) + (x2 >> 1), (y3 >> 1) + (y2 >> 1),
x3, y3);
}
static int makespline(Point *pts, Point *p, int n, GrPolyType)
{
register int i;
Point *gPp= pts;
gPp->x= p[0].x;
(gPp++)->y= p[0].y;
for (i= 0; i < n-1; i+= 3)
bez(&gPp, FIX(p[i+0].x), FIX(p[i+0].y), FIX(p[i+1].x), FIX(p[i+1].y),
FIX(p[i+2].x), FIX(p[i+2].y), FIX(p[i+3].x), FIX(p[i+3].y));
return gPp - pts;
}
void Port::DevStrokePolygon(Rectangle *r,
Point *pts, int npts, GrPolyType t, int psz, GrLineCap cap)
{
Point *tmppts= 0;
if (cap & eStartArrow)
DevDrawArrow(psz, pts[1]+r->origin, pts[0]+r->origin);
if (cap & eEndArrow)
DevDrawArrow(psz, pts[npts-2]+r->origin, pts[npts-1]+r->origin);
if (t & ePolyBezier) {
tmppts= (Point*) Alloca(sizeof(Point) * npts * 30);
int nn= makespline(tmppts, pts, npts, t);
if (nn > 0) {
pts= tmppts;
npts= nn;
}
}
DevStrokePolygon2(r, pts, npts, ePolyDefault, psz, cap);
Freea(tmppts);
}
void Port::DevFillPolygon(Rectangle *r, Point *pts, int npts, GrPolyType t)
{
Point *tmppts= 0;
if (t & ePolyBezier) {
tmppts= (Point*) Alloca(sizeof(Point) * npts * 30);
int nn= makespline(tmppts, pts, npts, t);
if (nn > 0) {
pts= tmppts;
npts= nn;
}
}
DevFillPolygon2(r, pts, npts, ePolyDefault);
Freea(tmppts);
}
void Port::DevStrokeWedge(int psz, GrLineCap cap, Rectangle *r, int s, int d)
{
int rpsz= (psz <= 0) ? 1 : psz;
rpsz*= 2;
if (rpsz >= r->extent.x || rpsz >= r->extent.y) {
DevFillWedge(r, s, d);
return;
}
if (r->extent.x < 4 || r->extent.y < 4) {
DevStrokeRect(psz, r);
return;
}
if (cap & eStartArrow) {
DevDrawArrow(psz, r->OvalAngleToPoint(s+10), r->OvalAngleToPoint(s));
// s+= 10; d-= 10;
}
if (cap & eEndArrow) {
DevDrawArrow(psz, r->OvalAngleToPoint(s+d-10), r->OvalAngleToPoint(s+d));
// d-= 10;
}
DevStrokeWedge2(psz, cap, r, s, d);
}
void Port::DevFillWedge(Rectangle *r, int s, int d)
{
if (r->extent.x < 4 || r->extent.y < 4)
DevFillRect(r);
else
DevFillWedge2(r, s, d);
}
static void Wedge2Polygon(Point *pts, int &n, Point e2, float start, float len)
{
if (len < 10) // consider it a line
pts[n++]= PolarToPoint(start, e2.x, e2.y) + e2;
else { // subdivide
Wedge2Polygon(pts, n, e2, start, len/2);
Wedge2Polygon(pts, n, e2, start+len/2, len/2);
}
}
void Port::DevStrokeWedge2(int psz, GrLineCap cap, Rectangle *r, int start, int len)
{
Point *pts= (Point*) Alloca(sizeof(Point) * len);
Point e2= (r->extent-psz)/2;
int n= 0;
Wedge2Polygon(pts, n, e2, start, len);
pts[n++]= PolarToPoint(start+len, e2.x, e2.y) + e2;
r->origin+= psz/2;
DevStrokePolygon2(r, pts, n, ePolyDefault, psz, cap);
Freea(pts);
}
void Port::DevFillWedge2(Rectangle *r, int start, int len)
{
Point *pts= (Point*) Alloca(sizeof(Point) * len);
Point e2= r->extent/2;
int n= 0;
pts[n++]= e2;
Wedge2Polygon(pts, n, e2, start, len);
pts[n++]= PolarToPoint(start+len, e2.x, e2.y) + e2;
DevFillPolygon2(r, pts, n, ePolyDefault);
Freea(pts);
}
void Port::DevStrokeRRect(int psz, Rectangle *r, Point dia)
{
int rpsz= (psz <= 0) ? 1 : psz;
rpsz*= 2;
if (rpsz >= r->extent.x || rpsz >= r->extent.y) {
DevFillRRect(r, dia);
return;
}
if (Math::Odd(dia.x)) // force to be even
dia.x--;
if (Math::Odd(dia.y)) // force to be even
dia.y--;
if (dia.x >= r->extent.x && dia.y >= r->extent.y)
DevStrokeOval(psz, r);
else
DevStrokeRRect2(psz, r, Min(dia, r->extent));
}
void Port::DevFillRRect(Rectangle *r, Point dia)
{
if (Math::Odd(dia.x)) // force to be even
dia.x--;
if (Math::Odd(dia.y)) // force to be even
dia.y--;
if (dia.x > r->extent.x && dia.y > r->extent.y)
DevFillOval(r);
else
DevFillRRect2(r, Min(dia, r->extent));
}
void Port::DevStrokeOval(int psz, Rectangle *r)
{
int rpsz= (psz <= 0) ? 1 : psz;
rpsz*= 2;
if (rpsz >= r->extent.x || rpsz >= r->extent.y)
DevFillOval(r);
else if (r->extent.x < 4 || r->extent.y < 4)
DevStrokeRect(psz, r);
else
DevStrokeOval2(psz, r);
}
void Port::DevFillOval(Rectangle *r)
{
if (r->extent.x < 4 || r->extent.y < 4)
DevFillRect(r);
else
DevFillOval2(r);
}
void Port::DevStrokeOval2(int psz, Rectangle *r)
{
DevStrokeRRect2(psz, r, r->extent);
}
void Port::DevFillOval2(Rectangle *r)
{
DevFillRRect2(r, r->extent);
}
//---- abstract methods --------------------------------------------------------
void Port::DevStrokeRect2(int, Rectangle*)
{
AbstractMethod("DevStrokeRect2");
}
void Port::DevStrokeRRect2(int, Rectangle*, Point)
{
AbstractMethod("DevStrokeRRect2");
}
void Port::DevFillRRect2(Rectangle*, Point)
{
AbstractMethod("DevFillRRect2");
}
void Port::DevStrokePolygon2(Rectangle*, Point*, int, GrPolyType, int, GrLineCap)
{
AbstractMethod("DevStrokePolygon2");
}
void Port::DevFillPolygon2(Rectangle*, Point*, int, GrPolyType)
{
AbstractMethod("DevFillPolygon2");
}
void Port::DevStrokeLine2(int, Rectangle*, GrLineCap, Point, Point)
{
AbstractMethod("DevStrokeLine2");
}
//---- Utilities ---------------------------------------------------------------
void DrawShadow(Rectangle &r, Point delta, Ink* fg, Ink *bg)
{
if (delta != gPoint0) {
Rectangle sr(r);
sr.origin.x+= sr.extent.x;
sr.origin.y+= delta.y;
sr.extent.x= delta.x;
GrPaintRect(sr, bg);
sr= r;
sr.origin.x+= delta.x;
sr.origin.y+= sr.extent.y;
sr.extent.y= delta.y;
GrPaintRect(sr, bg);
}
GrPaintRect(r, fg);
GrStrokeRect(r);
}